# MessageBus - Heart of the backend messaging system
# Copyright (C) 2007 Lauri Taimila
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

import logging
import threading

from message import Message
from message_handler import MessageHandler
from message_type_priority import MessageType, MessagePriority

__licence__ = "GPLv2"
__copyright__ = "2007, Lauri Taimila"
__author__ = "Lauri Taimila <lauri@taimila.com>"

class MessageBus:
    """
    MessageBus is the heart of the backend messaging system.
    
    Almost all communication between components goes through this MessageBus.
    Components communicate with Messages, which are delivered to MessageHandlers
    via MessageBus. MessageBus knows which MessageHandlers are interested in
    which type of Messages. MessageBus is also aware of MessageHandler
    priorities and this way can serve high priority components first.
    
    When MessageHandler is registered to the MessageBus there is also another
    parameter besides handler itself. Second parameter is a dictionary that
    defines MessageTypes that registered handler wants to be notified of and
    also priorities for those message types.
    
    Example of second parameter:
            dict = {MessageType.FRONTEND_OPENED : MessagePriority.LOW ,
                    MessageType.FRONTEND_CLOSED : MessagePriority.NORMAL }
    """  
    
    # This determines number of message types avaialble. In other words, this
    # variable tells how many variables is defined in MessageType class.
    NUMBER_OF_MESSAGE_TYPES = len([k for k,v in vars(MessageType).items() if type(v) is int])
    
    def __init__(self, logger):
        """
        Create a new MessageBus object.
        @param logger: Logger object
        """
        # MessageHandlers - index is MessageType and data is a list of
        # tuples (priority, MessageHandler object) that is sorted by priorities.
        self.message_handlers = [[] for i in range(self.__class__.NUMBER_OF_MESSAGE_TYPES)]
        self.lock = threading.Lock()
        self.logger = logger
    
    def registerMessageHandler(self, message_handler, message_priority_list):
        """
        Register a new MessageHandler to this MessageBus
        @param message_handler: MessageHandler object
        @param message_priority_list: Priority list for this MessageHandler
        """
        if isinstance(message_handler, MessageHandler):
            for key in message_priority_list:
                rule = (message_priority_list[key], message_handler)
                self.message_handlers[key].append(rule)
                self.message_handlers[key].sort() # Keep priority order
        else:
            self.logger.critical("MessageHandler registration failed. Object " +
                                 repr(message_handler) +" is invalid type.")
            raise TypeError("Only MessageHandlers can be registered!")
        self.logger.debug("MessageHandler '" + message_handler.getName() + "' registered to the message bus.")
        
    def unregisterMessageHandler(self, message_handler):
        """
        Unregister MessageHandler form this MessageBus.
        @param message_handler: MessageHandler object that should be removed from bus
        """
        if isinstance(message_handler, MessageHandler):
            for i in range(self.__class__.NUMBER_OF_MESSAGE_TYPES):
                if len(self.message_handlers[i]) != 0:
                    rules = self.message_handlers[i]
                    for element in rules:
                        if element[1] is message_handler:
                            del element
        else:
            raise TypeError("Only MessageHandlers can be unregistered!")
        self.logger.debug("MessageHandler '" + message_handler.getName() + 
                          "' unregistered from the message bus.")
        
    def unregisterAllMessageHandlers(self):
         """
         Unregisters all MessageHandler from this MessageBus.
         """
         self.message_handlers[:] = []
        
    def notifyMessage(self, message):
        """
        Emit a new Message to this MessageBus.
        @param message: Message object
        """
        if isinstance(message, Message):
            self.lock.acquire() # Lock messagebus
            self.logger.debug("Message bus locked. Message of type '"+ 
                              str(message.get_type()) + "' is on the bus.")
            handler_list = self.message_handlers[message.get_type()]
            for element in handler_list:
                element[1].handleMessage(message)
            self.lock.release() # Release messagebus lock
        else:
            self.logger.error("TypeError occured when message was notified to the bus.")
            raise TypeError("Notified message must be instances of 'Message' type")
        
